/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ania2;

import ania2.engine.utils.ZobristHashTables;
import ania2.enums.Fields;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author pawel
 */
public class BrazilianMoveGenerator2 implements MoveGenerator {

    private static final long PERMITTED = BoardPosition.getPermitted();
    private final Fields[] fields = Fields.values();
    private final int[] directions = new int[]{-4, -5, 4, 5};
    private static final long WHITE_PROMOTION_LINE = (1L << 26) | (1L << 25) | (1L << 24) | (1L << 23);
    private static final long BLACK_PROMOTION_LINE = (1L << 58) | (1L << 57) | (1L << 56) | (1L << 55);
    private int longestCapture;
    //przekątne "w prawo"
    private static final long DIAGONAL_G1H2 = (1L << 55) | (1L << 50);
    private static final long DIAGONAL_E1H4 = (1L << 56) | (1L << 51) | (1L << 46) | (1L << 41);
    private static final long DIAGONAL_C1H6 = (1L << 57) | (1L << 52) | (1L << 47) | (1L << 42) | (1L << 37) | (1L << 32);
    private static final long DIAGONAL_A1H8 = (1L << 58) | (1L << 53) | (1L << 48) | (1L << 43) | (1L << 38) | (1L << 33) | (1L << 28) | (1L << 23);
    private static final long DIAGONAL_A3F8 = (1L << 49) | (1L << 44) | (1L << 39) | (1L << 34) | (1L << 29) | (1L << 24);
    private static final long DIAGONAL_A5D8 = (1L << 40) | (1L << 35) | (1L << 30) | (1L << 25);
    private static final long DIAGONAL_A7B8 = (1L << 31) | (1L << 26);
    //przekątne "w lewo"
    private static final long DIAGONAL_C1A3 = (1L << 57) | (1L << 53) | (1L << 49);
    private static final long DIAGONAL_E1A5 = (1L << 56) | (1L << 52) | (1L << 48) | (1L << 44) | (1L << 40);
    private static final long DIAGONAL_G1A7 = (1L << 55) | (1L << 51) | (1L << 47) | (1L << 43) | (1L << 39) | (1L << 35) | (1L << 31);
    private static final long DIAGONAL_H2B8 = (1L << 50) | (1L << 46) | (1L << 42) | (1L << 38) | (1L << 34) | (1L << 30) | (1L << 26);
    private static final long DIAGONAL_H4D8 = (1L << 41) | (1L << 37) | (1L << 33) | (1L << 29) | (1L << 25);
    private static final long DIAGONAL_H6F8 = (1L << 32) | (1L << 28) | (1L << 24);
    private static final long[] RIGHT_DIAGONALS = new long[64];
    private static final long[] LEFT_DIAGONALS = new long[64];
    private static final long[][] HASH_RANDOM_TABLE = ZobristHashTables.HASH_CODE_TABLE;
    private static final long[][] LOCK_RANDOM_TABLE = ZobristHashTables.LOCK_CODE_TABLE;


    static {
        initDiagonals();
    }

    private static void initDiagonals() {
        RIGHT_DIAGONALS[8] = RIGHT_DIAGONALS[13] = DIAGONAL_G1H2;
        for (int i = 7; i <= 22; i += 5) {
            RIGHT_DIAGONALS[i] = DIAGONAL_E1H4;
        }
        for (int i = 6; i <= 31; i += 5) {
            RIGHT_DIAGONALS[i] = DIAGONAL_C1H6;
        }
        for (int i = 5; i <= 40; i += 5) {
            RIGHT_DIAGONALS[i] = DIAGONAL_A1H8;
        }
        for (int i = 14; i <= 39; i += 5) {
            RIGHT_DIAGONALS[i] = DIAGONAL_A3F8;
        }
        for (int i = 23; i <= 38; i += 5) {
            RIGHT_DIAGONALS[i] = DIAGONAL_A5D8;
        }
        RIGHT_DIAGONALS[32] = RIGHT_DIAGONALS[37] = DIAGONAL_A7B8;

        for (int i = 6; i <= 14; i += 4) {
            LEFT_DIAGONALS[i] = DIAGONAL_C1A3;
        }
        for (int i = 7; i <= 23; i += 4) {
            LEFT_DIAGONALS[i] = DIAGONAL_E1A5;
        }
        for (int i = 8; i <= 32; i += 4) {
            LEFT_DIAGONALS[i] = DIAGONAL_G1A7;
        }
        for (int i = 13; i <= 37; i += 4) {
            LEFT_DIAGONALS[i] = DIAGONAL_H2B8;
        }
        for (int i = 22; i <= 38; i += 4) {
            LEFT_DIAGONALS[i] = DIAGONAL_H4D8;
        }
        for (int i = 31; i <= 39; i += 4) {
            LEFT_DIAGONALS[i] = DIAGONAL_H6F8;
        }
    }

    public List<Move> generateCaptures(BoardPosition position, boolean whiteTurn) {
        LinkedList<Move> moves = new LinkedList<Move>();
        long whitePieces = position.getWhitePieces();
        long blackPieces = position.getBlackPieces();
        long queens = position.getQueens();
        int hashCode = position.getHashCode();
        //int lockCode = position.getLockCode();
        longestCapture = 0;
        if (whiteTurn) {
            generateWhiteQueenCaptures(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
            generateWhiteCaptures(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
            if (!moves.isEmpty()) {
                removeRedundantCaptures(moves, longestCapture);
            }
        } else {
            generateBlackQueenCaptures(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
            generateBlackCaptures(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
            if (!moves.isEmpty()) {
                removeRedundantCaptures(moves, longestCapture);
            }
        }
        return moves;
    }

    public List<Move> generateMoves(BoardPosition position, boolean whiteTurn) {
        LinkedList<Move> moves = new LinkedList<Move>();
        long whitePieces = position.getWhitePieces();
        long blackPieces = position.getBlackPieces();
        long queens = position.getQueens();
        int hashCode = position.getHashCode();
        //int lockCode = position.getLockCode();
        /*position.computeHashAndLockCode();
        int h = position.getHashCode();
        int l = position.getLockCode();
        if(hashCode!=h || l!=lockCode)
            System.err.println("nie jest dobrze");*/
        longestCapture = 0;
        if (whiteTurn) {
            generateWhiteQueenCaptures(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
            generateWhiteCaptures(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
            if (moves.isEmpty()) {
                generateWhiteQueenMoves(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
                generateSimpleWhiteMoves(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
            } else {
                removeRedundantCaptures(moves, longestCapture);
            }
        } else {
            generateBlackQueenCaptures(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
            generateBlackCaptures(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
            if (moves.isEmpty()) {
                generateBlackQueenMoves(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
                generateSimpleBlackMoves(whitePieces, blackPieces, queens, moves, hashCode/*, lockCode*/);
            } else {
                removeRedundantCaptures(moves, longestCapture);
            }
        }

        /*for(Move m : moves) {
            BoardPosition p = m.getPosition();
            int hash = p.getHashCode();
            int lock = p.getLockCode();
            p.computeHashAndLockCode();
            if(p.getHashCode()!=hash)
                System.err.println("hasz");
            if(p.getLockCode()!=lock)
                System.err.println("lock");
        }*/
        return moves;
    }

    private void generateSimpleWhiteMoves(long white, long black, long queens, List<Move> moves, int hashCode/*, lockCode*/) {
        long unoccupied = ~(white | black) & PERMITTED;
        int leftDirection = directions[2];
        int rightDirection = directions[3];
        //piony, które mogą się ruszyć w lewo
        long pieces1 = Long.rotateLeft(unoccupied, leftDirection) & (white & ~queens);
        //piony, które mogą się ruszyć w prawo
        long pieces2 = Long.rotateLeft(unoccupied, rightDirection) & (white & ~queens);
        long sourcePiece1 = Long.highestOneBit(pieces1);
        long sourcePiece2 = Long.highestOneBit(pieces2);
        //generowanie ruchów w lewo
        while (sourcePiece1 > 0) {
            pieces1 ^= sourcePiece1;
            long targetField = Long.rotateRight(sourcePiece1, leftDirection);
            long newWhite = white ^ (sourcePiece1 | targetField);
            long newQueens = queens;
            int sourceIndex = Long.numberOfLeadingZeros(sourcePiece1);
            int targetIndex = Long.numberOfLeadingZeros(targetField);
            Fields from = fields[sourceIndex];
            Fields to = fields[targetIndex];
            String notation = from.toString() + "-" + to.toString();

            boolean promotion = false;
            if (notation.charAt(notation.length() - 1) == '8') {
                newQueens |= (newWhite & WHITE_PROMOTION_LINE);
                promotion = true;
            }
            int newHashCode = hashCode;
            //int newLockCode = lockCode;
            long newLockCode = (newWhite+black+newQueens);
            BoardPosition newPosition = new BoardPosition(newWhite, black, newQueens);
            int type = (promotion) ? 2 : 0;
            //generowanie kodów haszujących
            newHashCode ^= HASH_RANDOM_TABLE[sourceIndex][0] ^ HASH_RANDOM_TABLE[targetIndex][type];
            //newLockCode ^= LOCK_RANDOM_TABLE[sourceIndex][0] ^ LOCK_RANDOM_TABLE[targetIndex][type];
            newPosition.setHashCode(newHashCode);
            newPosition.setLockCode(newLockCode);
            Move m = new Move(newPosition, from, to, notation, notation, promotion);
            moves.add(m);
            sourcePiece1 = Long.highestOneBit(pieces1);
        }
        //generowanie ruchów w prawo
        while (sourcePiece2 > 0) {
            pieces2 ^= sourcePiece2;
            long targetField = Long.rotateRight(sourcePiece2, rightDirection);
            long newWhite = white ^ (sourcePiece2 | targetField);
            long newQueens = queens;
            int sourceIndex = Long.numberOfLeadingZeros(sourcePiece2);
            int targetIndex = Long.numberOfLeadingZeros(targetField);
            Fields from = fields[sourceIndex];
            Fields to = fields[targetIndex];
            String notation = from.toString() + "-" + to.toString();

            boolean promotion = false;
            if (notation.charAt(notation.length() - 1) == '8') {
                newQueens |= (newWhite & WHITE_PROMOTION_LINE);
                promotion = true;
            }
            int newHashCode = hashCode;
            //int newLockCode = lockCode;
            long newLockCode = (newWhite + black + newQueens);
            BoardPosition newPosition = new BoardPosition(newWhite, black, newQueens);
            int type = (promotion) ? 2 : 0;
            //generowanie kodów haszujących
            newHashCode ^= HASH_RANDOM_TABLE[sourceIndex][0] ^ HASH_RANDOM_TABLE[targetIndex][type];
            //newLockCode ^= LOCK_RANDOM_TABLE[sourceIndex][0] ^ LOCK_RANDOM_TABLE[targetIndex][type];
            newPosition.setHashCode(newHashCode);
            newPosition.setLockCode(newLockCode);           
            Move m = new Move(newPosition, from, to, notation, notation, promotion);
            moves.add(m);
            sourcePiece2 = Long.highestOneBit(pieces2);
        }
    }

    private void generateSimpleBlackMoves(long white, long black, long queens, List<Move> moves, int hashCode/*, lockCode*/) {
        long unoccupied = ~(white | black) & PERMITTED;
        int leftDirection = directions[1];
        int rightDirection = directions[0];
        //piony, które mogą się ruszyć w lewo
        long pieces1 = Long.rotateLeft(unoccupied, leftDirection) & (black & ~queens);
        //piony, które mogą się ruszyć w prawo
        long pieces2 = Long.rotateLeft(unoccupied, rightDirection) & (black & ~queens);
        long sourcePiece1 = Long.highestOneBit(pieces1);
        long sourcePiece2 = Long.highestOneBit(pieces2);
        //generowanie ruchów w lewo
        while (sourcePiece1 > 0) {
            pieces1 ^= sourcePiece1;
            long targetField = Long.rotateRight(sourcePiece1, leftDirection);
            long newBlack = black ^ (sourcePiece1 | targetField);
            long newQueens = queens;
            int sourceIndex = Long.numberOfLeadingZeros(sourcePiece1);
            int targetIndex = Long.numberOfLeadingZeros(targetField);
            Fields from = fields[sourceIndex];
            Fields to = fields[targetIndex];
            String notation = from.toString() + "-" + to.toString();

            boolean promotion = false;
            if (notation.charAt(notation.length() - 1) == '1') {
                newQueens |= (newBlack & BLACK_PROMOTION_LINE);
                promotion = true;
            }
            int newHashCode = hashCode;
            //int newLockCode = lockCode;
            long newLockCode = (white + newBlack + newQueens);
            BoardPosition newPosition = new BoardPosition(white, newBlack, newQueens);
            int type = (promotion) ? 3 : 1;
            //generowanie kodów haszujących
            newHashCode ^= HASH_RANDOM_TABLE[sourceIndex][1] ^ HASH_RANDOM_TABLE[targetIndex][type];
            //newLockCode ^= LOCK_RANDOM_TABLE[sourceIndex][1] ^ LOCK_RANDOM_TABLE[targetIndex][type];
            newPosition.setHashCode(newHashCode);
            newPosition.setLockCode(newLockCode);
            Move m = new Move(newPosition, from, to, notation, notation, promotion);
            moves.add(m);
            sourcePiece1 = Long.highestOneBit(pieces1);
        }
        //generowanie ruchów w prawo
        while (sourcePiece2 > 0) {
            pieces2 ^= sourcePiece2;
            long targetField = Long.rotateRight(sourcePiece2, rightDirection);
            long newBlack = black ^ (sourcePiece2 | targetField);
            long newQueens = queens;
            int sourceIndex = Long.numberOfLeadingZeros(sourcePiece2);
            int targetIndex = Long.numberOfLeadingZeros(targetField);
            Fields from = fields[sourceIndex];
            Fields to = fields[targetIndex];
            String notation = from.toString() + "-" + to.toString();

            boolean promotion = false;
            if (notation.charAt(notation.length() - 1) == '1') {
                newQueens |= (newBlack & BLACK_PROMOTION_LINE);
                promotion = true;
            }
            int newHashCode = hashCode;
            //int newLockCode = lockCode;
            long newLockCode = (white+newBlack+newQueens);
            BoardPosition newPosition = new BoardPosition(white, newBlack, newQueens);
            int type = (promotion) ? 3 : 1;
            //generowanie kodów haszujących
            newHashCode ^= HASH_RANDOM_TABLE[sourceIndex][1] ^ HASH_RANDOM_TABLE[targetIndex][type];
            //newLockCode ^= LOCK_RANDOM_TABLE[sourceIndex][1] ^ LOCK_RANDOM_TABLE[targetIndex][type];
            newPosition.setHashCode(newHashCode);
            newPosition.setLockCode(newLockCode);
            Move m = new Move(newPosition, from, to, notation, notation, promotion);
            moves.add(m);
            sourcePiece2 = Long.highestOneBit(pieces2);
        }
    }

    private void generateWhiteCaptures(long white, long black, long queens, List<Move> moves,
            int hashCode/*, lockCode*/) {
        long unoccupied = ~(white | black) & PERMITTED;
        for (int dir : directions) {
            long neighbour = Long.rotateLeft(unoccupied, dir) & black;
            long source = Long.rotateLeft(neighbour, dir) & (white & ~queens);
            long sourcePiece = Long.highestOneBit(source);
            while (sourcePiece > 0) {
                source ^= sourcePiece;
                int index = Long.numberOfLeadingZeros(sourcePiece);
                int targetIndex = index + 2 * dir;
                long captured = Long.rotateRight(sourcePiece, dir);
               // int capturedIndex = Long.numberOfLeadingZeros(captured);
                int capturedIndex = index + dir;
                long targetField = Long.rotateRight(sourcePiece, 2 * dir);
                long newWhite = white ^ (sourcePiece | targetField);
                long newBlack = black ^ captured;
                long newQueens = queens & (newWhite | newBlack);
                String notation = fields[index].toString() + "x" + fields[targetIndex].toString();
                int newHashCode = hashCode;
                //int newLockCode = lockCode;
                int capturedType = ((captured & queens) == 0) ? 1 : 3;
                newHashCode ^= HASH_RANDOM_TABLE[index][0] ^ HASH_RANDOM_TABLE[capturedIndex][capturedType];
                //newLockCode ^= LOCK_RANDOM_TABLE[index][0] ^ LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                nextWhiteCapture(targetField, newWhite, newBlack, newQueens, notation, moves, -dir, newHashCode/*, lockCode*/);
                sourcePiece = Long.highestOneBit(source);
            }
        }
    }

    private void nextWhiteCapture(long piece, long white, long black, long queens,
            String notation, List<Move> moves, int forbiddenDirection, int hashCode/*, lockCode*/) {
        boolean finalCapture = true;
        long unoccupied = ~(white | black) & PERMITTED;
        int sourceIndex = Long.numberOfLeadingZeros(piece);
        for (int dir : directions) {
            if (dir == forbiddenDirection) {
                continue;
            }
            long captured = Long.rotateRight(piece, dir) & black;
            long targetField = Long.rotateRight(captured, dir) & unoccupied;
            if (targetField != 0) {
                finalCapture = false;
                int targetIndex = Long.numberOfLeadingZeros(targetField);
                int capturedIndex = Long.numberOfLeadingZeros(captured);
                long newWhite = white ^ (piece | targetField);
                long newBlack = black ^ captured;
                long newQueens = queens & (newWhite | newBlack);
                String newNotation = notation + "x" + fields[targetIndex].toString();
                int newHashCode = hashCode;
                //int newLockCode = lockCode;
                int capturedType = ((captured & queens) == 0) ? 1 : 3;
                newHashCode ^= HASH_RANDOM_TABLE[capturedIndex][capturedType];
                //newLockCode ^= LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                nextWhiteCapture(targetField, newWhite, newBlack, newQueens, newNotation, moves, -dir, newHashCode/*, lockCode*/);
            }
        }

        if (finalCapture) {
            if (notation.length() < longestCapture) {
                return;
            } else {
                longestCapture = notation.length();
            }
            Fields source = Fields.valueOf(notation.substring(0, 2));
            Fields target = Fields.valueOf(notation.substring(notation.length() - 2, notation.length()));
            String shortNotation = source.toString() + "x" + target.toString();
            //piece to queen promotion
            boolean promotion = false;
            if (shortNotation.charAt(shortNotation.length() - 1) == '8') {
                queens |= (white & WHITE_PROMOTION_LINE);
                promotion = true;
                hashCode ^= HASH_RANDOM_TABLE[sourceIndex][2];
                //lockCode ^= LOCK_RANDOM_TABLE[sourceIndex][2];
            }
            else {
                hashCode ^= HASH_RANDOM_TABLE[sourceIndex][0];
                //lockCode ^= LOCK_RANDOM_TABLE[sourceIndex][0];
            }

            BoardPosition position = new BoardPosition(white, black, queens);
            long lockCode = (white+black+queens);
            position.setHashCode(hashCode);
            position.setLockCode(lockCode);
            Move m = new Move(position, source, target, notation, shortNotation, promotion);
            if (promotion) {
                moves.add(0, m);
            } else {
                moves.add(m);
            }
        }
    }

    private void generateBlackCaptures(long white, long black, long queens, List<Move> moves, int hashCode/*, lockCode*/) {
        long unoccupied = ~(white | black) & PERMITTED;
        for (int dir : directions) {
            long neighbour = Long.rotateLeft(unoccupied, dir) & white;
            long source = Long.rotateLeft(neighbour, dir) & (black & ~queens);
            long sourcePiece = Long.highestOneBit(source);
            while (sourcePiece > 0) {
                source ^= sourcePiece;
                int index = Long.numberOfLeadingZeros(sourcePiece);
                int targetIndex = index + 2 * dir;
                long captured = Long.rotateRight(sourcePiece, dir);
                int capturedIndex = index + dir;
                long targetField = Long.rotateRight(sourcePiece, 2 * dir);
                long newWhite = white ^ (Long.rotateRight(sourcePiece, dir));
                long newBlack = black ^ (sourcePiece | targetField);
                long newQueens = queens & (newWhite | newBlack);
                String notation = fields[index].toString() + "x" + fields[targetIndex].toString();
                int newHashCode = hashCode;
                //int newLockCode = lockCode;
                int capturedType = ((captured & queens) == 0) ? 0 : 2;
                newHashCode ^= HASH_RANDOM_TABLE[index][1] ^ HASH_RANDOM_TABLE[capturedIndex][capturedType];
                //newLockCode ^= LOCK_RANDOM_TABLE[index][1] ^ LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                nextBlackCapture(targetField, newWhite, newBlack, newQueens, notation, moves, -dir, newHashCode/*, lockCode*/);
                sourcePiece = Long.highestOneBit(source);
            }
        }
    }

    private void nextBlackCapture(long piece, long white, long black, long queens,
            String notation, List<Move> moves, int forbiddenDirection, int hashCode/*, lockCode*/) {
        boolean finalCapture = true;
        long unoccupied = ~(white | black) & PERMITTED;
        int sourceIndex = Long.numberOfLeadingZeros(piece);
        for (int dir : directions) {
            if (dir == forbiddenDirection) {
                continue;
            }
            long captured = Long.rotateRight(piece, dir) & white;
            long targetField = Long.rotateRight(captured, dir) & unoccupied;
            if (targetField != 0) {
                finalCapture = false;
                int targetIndex = Long.numberOfLeadingZeros(targetField);
                int capturedIndex = Long.numberOfLeadingZeros(captured);
                long newWhite = white ^ captured;
                long newBlack = black ^ (piece | targetField);
                long newQueens = queens & (newWhite | newBlack);
                String newNotation = notation + "x" + fields[targetIndex].toString();
                int newHashCode = hashCode;
                //int newLockCode = lockCode;
                int capturedType = ((captured & queens) == 0) ? 0 : 2;
                newHashCode ^= HASH_RANDOM_TABLE[capturedIndex][capturedType];
                //newLockCode ^= LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                nextBlackCapture(targetField, newWhite, newBlack, newQueens, newNotation, moves, -dir, newHashCode/*, lockCode*/);
            }
        }

        if (finalCapture) {
            if (notation.length() < longestCapture) {
                return;
            } else {
                longestCapture = notation.length();
            }
            Fields source = Fields.valueOf(notation.substring(0, 2));
            Fields target = Fields.valueOf(notation.substring(notation.length() - 2, notation.length()));
            String shortNotation = source.toString() + "x" + target.toString();
            //piece to queen promotion
            boolean promotion = false;
            if (shortNotation.charAt(shortNotation.length() - 1) == '1') {
                queens |= (black & BLACK_PROMOTION_LINE);
                promotion = true;
                hashCode ^= HASH_RANDOM_TABLE[sourceIndex][3];
                //lockCode ^= LOCK_RANDOM_TABLE[sourceIndex][3];
            }
            else {
                hashCode ^= HASH_RANDOM_TABLE[sourceIndex][1];
                //lockCode ^= LOCK_RANDOM_TABLE[sourceIndex][1];
            }

            BoardPosition position = new BoardPosition(white, black, queens);
            long lockCode = (white+black+queens);
            position.setHashCode(hashCode);
            position.setLockCode(lockCode);
            Move m = new Move(position, source, target, notation, shortNotation, promotion);
            if (promotion) {
                moves.add(0, m);
            } else {
                moves.add(m);
            }
        }

    }

    private void generateWhiteQueenCaptures(long white, long black, long queens, List<Move> moves,
            int hashCode/*, lockCode*/) {
        long whiteQueens = white & queens;
        if (Long.highestOneBit(whiteQueens) == 0) {
            return;
        }
        long unoccupied = ~(white | black) & PERMITTED;
        long queen = Long.highestOneBit(whiteQueens);
        while (queen > 0) {
            whiteQueens ^= queen;
            int index = Long.numberOfLeadingZeros(queen);
            long rightDiagonal = RIGHT_DIAGONALS[index];
            long leftDiagonal = LEFT_DIAGONALS[index];
            long opponentRightDiag = black & rightDiagonal;
            long opponentLeftDiag = black & leftDiagonal;
            long freeRightDiag = unoccupied & rightDiagonal;
            long freeLeftDiag = unoccupied & leftDiagonal;

            //w prawo do góry
            if (Long.lowestOneBit(opponentRightDiag) < queen && freeRightDiag != 0) {
                long tmp = Long.rotateRight(queen, 5);
                while ((tmp & freeRightDiag) != 0) {
                    tmp = Long.rotateRight(tmp, 5);
                }
                if ((tmp & opponentRightDiag) == tmp) {
                    int capturedIndex = Long.numberOfLeadingZeros(tmp);
                    int capturedType = ((tmp & queens)==0) ? 1 : 3;
                    int newHashCode = hashCode;
                    //int newLockCode = lockCode;
                    newHashCode ^= HASH_RANDOM_TABLE[index][2] ^ HASH_RANDOM_TABLE[capturedIndex][capturedType];
                    //newLockCode ^= LOCK_RANDOM_TABLE[index][2] ^ LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                    long tmp2 = Long.rotateRight(tmp, 5) & freeRightDiag;
                    while (tmp2 != 0) {
                        //bicie piona tmp na tmp2
                        long newWhite = white ^ (queen | tmp2);
                        long newBlack = black ^ tmp;
                        long newQueens = queens ^ (queen | (tmp & queens) | tmp2);
                        int targetIndex = Long.numberOfLeadingZeros(tmp2);
                        String notation = fields[index].toString() + "x" + fields[targetIndex].toString();
                        nextWhiteQueenCapture(tmp2, newWhite, newBlack, newQueens, notation, moves, -5, newHashCode/*, newLockCode*/);
                        tmp2 = Long.rotateRight(tmp2, 5) & freeRightDiag;
                    }
                }
            }
            //w lewo do dołu
            if (Long.highestOneBit(opponentRightDiag) > queen && freeRightDiag != 0) {
                long tmp = Long.rotateLeft(queen, 5);
                while ((tmp & freeRightDiag) != 0) {
                    tmp = Long.rotateLeft(tmp, 5);
                }
                if ((tmp & opponentRightDiag) == tmp) {
                    int capturedIndex = Long.numberOfLeadingZeros(tmp);
                    int capturedType = ((tmp & queens)==0) ? 1 : 3;
                    int newHashCode = hashCode;
                    //int newLockCode = lockCode;
                    newHashCode ^= HASH_RANDOM_TABLE[index][2] ^ HASH_RANDOM_TABLE[capturedIndex][capturedType];
                    //newLockCode ^= LOCK_RANDOM_TABLE[index][2] ^ LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                    long tmp2 = Long.rotateLeft(tmp, 5) & freeRightDiag;
                    while (tmp2 != 0) {
                        //bicie piona tmp na tmp2
                        long newWhite = white ^ (queen | tmp2);
                        long newBlack = black ^ tmp;
                        long newQueens = queens ^ (queen | (tmp & queens) | tmp2);
                        int targetIndex = Long.numberOfLeadingZeros(tmp2);
                        String notation = fields[index].toString() + "x" + fields[targetIndex].toString();
                        nextWhiteQueenCapture(tmp2, newWhite, newBlack, newQueens, notation, moves, 5, newHashCode/*, newLockCode*/);
                        tmp2 = Long.rotateLeft(tmp2, 5) & freeRightDiag;
                    }
                }
            }
            //w lewo do góry
            if (Long.lowestOneBit(opponentLeftDiag) < queen && freeLeftDiag != 0) {
                long tmp = Long.rotateRight(queen, 4);
                while ((tmp & freeLeftDiag) != 0) {
                    tmp = Long.rotateRight(tmp, 4);
                }
                if ((tmp & opponentLeftDiag) == tmp) {
                    int capturedIndex = Long.numberOfLeadingZeros(tmp);
                    int capturedType = ((tmp & queens)==0) ? 1 : 3;
                    int newHashCode = hashCode;
                    //int newLockCode = lockCode;
                    newHashCode ^= HASH_RANDOM_TABLE[index][2] ^ HASH_RANDOM_TABLE[capturedIndex][capturedType];
                    //newLockCode ^= LOCK_RANDOM_TABLE[index][2] ^ LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                    long tmp2 = Long.rotateRight(tmp, 4) & freeLeftDiag;
                    while (tmp2 != 0) {
                        //bicie piona tmp na tmp2
                        long newWhite = white ^ (queen | tmp2);
                        long newBlack = black ^ tmp;
                        long newQueens = queens ^ (queen | (tmp & queens) | tmp2);
                        int targetIndex = Long.numberOfLeadingZeros(tmp2);
                        String notation = fields[index].toString() + "x" + fields[targetIndex].toString();
                        nextWhiteQueenCapture(tmp2, newWhite, newBlack, newQueens, notation, moves, -4, newHashCode/*, newLockCode*/);
                        tmp2 = Long.rotateRight(tmp2, 4) & freeLeftDiag;
                    }
                }
            }
            //w prawo do dołu
            if (Long.highestOneBit(opponentLeftDiag) > queen && freeLeftDiag != 0) {
                long tmp = Long.rotateLeft(queen, 4);
                while ((tmp & freeLeftDiag) != 0) {
                    tmp = Long.rotateLeft(tmp, 4);
                }
                if ((tmp & opponentLeftDiag) == tmp) {
                    int capturedIndex = Long.numberOfLeadingZeros(tmp);
                    int capturedType = ((tmp & queens)==0) ? 1 : 3;
                    int newHashCode = hashCode;
                    //int newLockCode = lockCode;
                    newHashCode ^= HASH_RANDOM_TABLE[index][2] ^ HASH_RANDOM_TABLE[capturedIndex][capturedType];
                    //newLockCode ^= LOCK_RANDOM_TABLE[index][2] ^ LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                    long tmp2 = Long.rotateLeft(tmp, 4) & freeLeftDiag;
                    while (tmp2 != 0) {
                        //bicie piona tmp na tmp2
                        long newWhite = white ^ (queen | tmp2);
                        long newBlack = black ^ tmp;
                        long newQueens = queens ^ (queen | (tmp & queens) | tmp2);
                        int targetIndex = Long.numberOfLeadingZeros(tmp2);
                        String notation = fields[index].toString() + "x" + fields[targetIndex].toString();
                        nextWhiteQueenCapture(tmp2, newWhite, newBlack, newQueens, notation, moves, 4, newHashCode/*, newLockCode*/);
                        tmp2 = Long.rotateLeft(tmp2, 4) & freeLeftDiag;
                    }
                }
            }
            queen = Long.highestOneBit(whiteQueens);
        }
    }

    private void nextWhiteQueenCapture(long queen, long white, long black, long queens, String notation,
            List<Move> moves, int forbiddenDirection, int hashCode/*, int lockCode*/) {
        boolean finalCapture = true;
        long unoccupied = ~(white | black) & PERMITTED;
        int index = Long.numberOfLeadingZeros(queen);
        long rightDiagonal = RIGHT_DIAGONALS[index];
        long leftDiagonal = LEFT_DIAGONALS[index];
        long opponentRightDiag = black & rightDiagonal;
        long opponentLeftDiag = black & leftDiagonal;
        long freeRightDiag = unoccupied & rightDiagonal;
        long freeLeftDiag = unoccupied & leftDiagonal;
        long opponentDiag;
        long freeDiag;
        for (int dir : directions) {
            if (dir == forbiddenDirection) {
                continue;
            }
            if (Math.abs(dir) == 5) {
                opponentDiag = opponentRightDiag;
                freeDiag = freeRightDiag;
            } else {
                opponentDiag = opponentLeftDiag;
                freeDiag = freeLeftDiag;
            }
            boolean isOpponentInDirection = (dir > 0) ? Long.lowestOneBit(opponentDiag) < queen : Long.highestOneBit(opponentDiag) > queen;
            if (isOpponentInDirection && freeDiag != 0) {
                long tmp = Long.rotateRight(queen, dir);
                while ((tmp & freeDiag) > 0) {
                    tmp = Long.rotateRight(tmp, dir);
                }
                if ((tmp & opponentDiag) == tmp) {
                    int capturedIndex = Long.numberOfLeadingZeros(tmp);
                    int capturedType = ((tmp & queens)==0) ? 1 : 3;
                    int newHashCode = hashCode;
                    //int newLockCode = lockCode;
                    newHashCode ^= HASH_RANDOM_TABLE[capturedIndex][capturedType];
                    //newLockCode ^= LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                    long tmp2 = Long.rotateRight(tmp, dir) & freeDiag;
                    while (tmp2 != 0) {
                        finalCapture = false;
                        long newWhite = white ^ (queen | tmp2);
                        long newBlack = black ^ tmp;
                        long newQueens = queens ^ (queen | (tmp & queens) | tmp2);
                        int targetIndex = Long.numberOfLeadingZeros(tmp2);
                        String newNotation = notation + "x" + fields[targetIndex].toString();
                        nextWhiteQueenCapture(tmp2, newWhite, newBlack, newQueens, newNotation, moves, -dir, newHashCode/*, newLockCode*/);
                        tmp2 = Long.rotateRight(tmp2, dir) & freeDiag;
                    }
                }
            }
        }

        if (finalCapture) {
            if (notation.length() < longestCapture) {
                return;
            } else {
                longestCapture = notation.length();
            }
            Fields source = Fields.valueOf(notation.substring(0, 2));
            Fields target = Fields.valueOf(notation.substring(notation.length() - 2, notation.length()));
            String shortNotation = source.toString() + "x" + target.toString();
            BoardPosition position = new BoardPosition(white, black, queens);
            hashCode ^= HASH_RANDOM_TABLE[index][2];
            //lockCode ^= LOCK_RANDOM_TABLE[index][2];
            long lockCode = (white+black+queens);
            position.setHashCode(hashCode);
            position.setLockCode(lockCode);
            //position.computeHashAndLockCode();
            Move m = new Move(position, source, target, notation, shortNotation, false);
            moves.add(m);
        }
    }

    private void generateBlackQueenCaptures(long white, long black, long queens, List<Move> moves,
            int hashCode/*, int lockCode*/) {
        long blackQueens = black & queens;
        if (Long.highestOneBit(blackQueens) == 0) {
            return;
        }
        long unoccupied = ~(white | black) & PERMITTED;
        long queen = Long.highestOneBit(blackQueens);
        while (queen > 0) {
            blackQueens ^= queen;
            int index = Long.numberOfLeadingZeros(queen);
            long rightDiagonal = RIGHT_DIAGONALS[index];
            long leftDiagonal = LEFT_DIAGONALS[index];
            long opponentRightDiag = white & rightDiagonal;
            long opponentLeftDiag = white & leftDiagonal;
            long freeRightDiag = unoccupied & rightDiagonal;
            long freeLeftDiag = unoccupied & leftDiagonal;

            //w prawo do góry
            if (Long.lowestOneBit(opponentRightDiag) < queen && freeRightDiag != 0) {
                long tmp = Long.rotateRight(queen, 5);
                while ((tmp & freeRightDiag) != 0) {
                    tmp = Long.rotateRight(tmp, 5);
                }
                if ((tmp & opponentRightDiag) == tmp) {
                    int capturedIndex = Long.numberOfLeadingZeros(tmp);
                    int capturedType = ((tmp & queens)==0) ? 0 : 2;
                    int newHashCode = hashCode;
                    //int newLockCode = lockCode;
                    newHashCode ^= HASH_RANDOM_TABLE[index][3] ^ HASH_RANDOM_TABLE[capturedIndex][capturedType];
                    //newLockCode ^= LOCK_RANDOM_TABLE[index][3] ^ LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                    long tmp2 = Long.rotateRight(tmp, 5) & freeRightDiag;
                    while (tmp2 != 0) {
                        //bicie piona tmp na tmp2
                        long newWhite = white ^ tmp;
                        long newBlack = black ^ (queen | tmp2);
                        long newQueens = queens ^ (queen | (tmp & queens) | tmp2);
                        int targetIndex = Long.numberOfLeadingZeros(tmp2);
                        String notation = fields[index].toString() + "x" + fields[targetIndex].toString();
                        nextBlackQueenCapture(tmp2, newWhite, newBlack, newQueens, notation, moves, -5, newHashCode/*, newLockCode*/);
                        tmp2 = Long.rotateRight(tmp2, 5) & freeRightDiag;
                    }
                }
            }
            //w lewo do dołu
            if (Long.highestOneBit(opponentRightDiag) > queen && freeRightDiag != 0) {
                long tmp = Long.rotateLeft(queen, 5);
                while ((tmp & freeRightDiag) != 0) {
                    tmp = Long.rotateLeft(tmp, 5);
                }
                if ((tmp & opponentRightDiag) == tmp) {
                    int capturedIndex = Long.numberOfLeadingZeros(tmp);
                    int capturedType = ((tmp & queens)==0) ? 0 : 2;
                    int newHashCode = hashCode;
                    //int newLockCode = lockCode;
                    newHashCode ^= HASH_RANDOM_TABLE[index][3] ^ HASH_RANDOM_TABLE[capturedIndex][capturedType];
                    //newLockCode ^= LOCK_RANDOM_TABLE[index][3] ^ LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                    long tmp2 = Long.rotateLeft(tmp, 5) & freeRightDiag;
                    while (tmp2 != 0) {
                        //bicie piona tmp na tmp2
                        long newWhite = white ^ tmp;
                        long newBlack = black ^ (queen | tmp2);
                        long newQueens = queens ^ (queen | (tmp & queens) | tmp2);
                        int targetIndex = Long.numberOfLeadingZeros(tmp2);
                        String notation = fields[index].toString() + "x" + fields[targetIndex].toString();
                        nextBlackQueenCapture(tmp2, newWhite, newBlack, newQueens, notation, moves, 5, newHashCode/*, newLockCode*/);
                        tmp2 = Long.rotateLeft(tmp2, 5) & freeRightDiag;
                    }
                }
            }
            //w lewo do góry
            if (Long.lowestOneBit(opponentLeftDiag) < queen && freeLeftDiag != 0) {
                long tmp = Long.rotateRight(queen, 4);
                while ((tmp & freeLeftDiag) != 0) {
                    tmp = Long.rotateRight(tmp, 4);
                }
                if ((tmp & opponentLeftDiag) == tmp) {
                    int capturedIndex = Long.numberOfLeadingZeros(tmp);
                    int capturedType = ((tmp & queens)==0) ? 0 : 2;
                    int newHashCode = hashCode;
                    //int newLockCode = lockCode;
                    newHashCode ^= HASH_RANDOM_TABLE[index][3] ^ HASH_RANDOM_TABLE[capturedIndex][capturedType];
                    //newLockCode ^= LOCK_RANDOM_TABLE[index][3] ^ LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                    long tmp2 = Long.rotateRight(tmp, 4) & freeLeftDiag;
                    while (tmp2 != 0) {
                        //bicie piona tmp na tmp2
                        long newWhite = white ^ tmp;
                        long newBlack = black ^ (queen | tmp2);
                        long newQueens = queens ^ (queen | (tmp & queens) | tmp2);
                        int targetIndex = Long.numberOfLeadingZeros(tmp2);
                        String notation = fields[index].toString() + "x" + fields[targetIndex].toString();
                        nextBlackQueenCapture(tmp2, newWhite, newBlack, newQueens, notation, moves, -4, newHashCode/*, newLockCode*/);
                        tmp2 = Long.rotateRight(tmp2, 4) & freeLeftDiag;
                    }
                }
            }
            //w prawo do dołu
            if (Long.highestOneBit(opponentLeftDiag) > queen && freeLeftDiag != 0) {
                long tmp = Long.rotateLeft(queen, 4);
                while ((tmp & freeLeftDiag) != 0) {
                    tmp = Long.rotateLeft(tmp, 4);
                }
                if ((tmp & opponentLeftDiag) == tmp) {
                    int capturedIndex = Long.numberOfLeadingZeros(tmp);
                    int capturedType = ((tmp & queens)==0) ? 0 : 2;
                    int newHashCode = hashCode;
                    //int newLockCode = lockCode;
                    newHashCode ^= HASH_RANDOM_TABLE[index][3] ^ HASH_RANDOM_TABLE[capturedIndex][capturedType];
                    //newLockCode ^= LOCK_RANDOM_TABLE[index][3] ^ LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                    long tmp2 = Long.rotateLeft(tmp, 4) & freeLeftDiag;
                    while (tmp2 != 0) {
                        //bicie piona tmp na tmp2
                        long newWhite = white ^ tmp;
                        long newBlack = black ^ (queen | tmp2);
                        long newQueens = queens ^ (queen | (tmp & queens) | tmp2);
                        int targetIndex = Long.numberOfLeadingZeros(tmp2);
                        String notation = fields[index].toString() + "x" + fields[targetIndex].toString();
                        nextBlackQueenCapture(tmp2, newWhite, newBlack, newQueens, notation, moves, 4, newHashCode/*, newLockCode*/);
                        tmp2 = Long.rotateLeft(tmp2, 4) & freeLeftDiag;
                    }
                }
            }
            queen = Long.highestOneBit(blackQueens);
        }
    }

    private void nextBlackQueenCapture(long queen, long white, long black, long queens, String notation,
            List<Move> moves, int forbiddenDirection, int hashCode/*, int lockCode*/) {
        boolean finalCapture = true;
        long unoccupied = ~(white | black) & PERMITTED;
        int index = Long.numberOfLeadingZeros(queen);
        long rightDiagonal = RIGHT_DIAGONALS[index];
        long leftDiagonal = LEFT_DIAGONALS[index];
        long opponentRightDiag = white & rightDiagonal;
        long opponentLeftDiag = white & leftDiagonal;
        long freeRightDiag = unoccupied & rightDiagonal;
        long freeLeftDiag = unoccupied & leftDiagonal;
        long opponentDiag;
        long freeDiag;
        for (int dir : directions) {
            if (dir == forbiddenDirection) {
                continue;
            }
            if (Math.abs(dir) == 5) {
                opponentDiag = opponentRightDiag;
                freeDiag = freeRightDiag;
            } else {
                opponentDiag = opponentLeftDiag;
                freeDiag = freeLeftDiag;
            }
            boolean isOpponentInDirection = (dir > 0) ? Long.lowestOneBit(opponentDiag) < queen : Long.highestOneBit(opponentDiag) > queen;
            if (isOpponentInDirection && freeDiag != 0) {
                long tmp = Long.rotateRight(queen, dir);
                while ((tmp & freeDiag) > 0) {
                    tmp = Long.rotateRight(tmp, dir);
                }
                if ((tmp & opponentDiag) == tmp) {
                    int capturedIndex = Long.numberOfLeadingZeros(tmp);
                    int capturedType = ((tmp & queens)==0) ? 0 : 2;
                    int newHashCode = hashCode;
                    //int newLockCode = lockCode;
                    newHashCode ^= HASH_RANDOM_TABLE[capturedIndex][capturedType];
                    //newLockCode ^= LOCK_RANDOM_TABLE[capturedIndex][capturedType];
                    long tmp2 = Long.rotateRight(tmp, dir) & freeDiag;
                    while (tmp2 != 0) {
                        finalCapture = false;
                        long newWhite = white ^ tmp;
                        long newBlack = black ^ (queen | tmp2);
                        long newQueens = queens ^ (queen | (tmp & queens) | tmp2);
                        int targetIndex = Long.numberOfLeadingZeros(tmp2);
                        String newNotation = notation + "x" + fields[targetIndex].toString();
                        nextBlackQueenCapture(tmp2, newWhite, newBlack, newQueens, newNotation, moves, -dir, newHashCode/*, newLockCode*/);
                        tmp2 = Long.rotateRight(tmp2, dir) & freeDiag;
                    }
                }
            }
        }

        if (finalCapture) {
            if (notation.length() < longestCapture) {
                return;
            } else {
                longestCapture = notation.length();
            }
            Fields source = Fields.valueOf(notation.substring(0, 2));
            Fields target = Fields.valueOf(notation.substring(notation.length() - 2, notation.length()));
            String shortNotation = source.toString() + "x" + target.toString();
            BoardPosition position = new BoardPosition(white, black, queens);
            hashCode ^= HASH_RANDOM_TABLE[index][3];
            //lockCode ^= LOCK_RANDOM_TABLE[index][3];
            long lockCode = (white+black+queens);
            position.setHashCode(hashCode);
            position.setLockCode(lockCode);
            //position.computeHashAndLockCode();
            Move m = new Move(position, source, target, notation, shortNotation, false);
            moves.add(m);
        }
    }

    private void generateWhiteQueenMoves(long white, long black, long queens, List<Move> moves,
            int hashCode/*, int lockCode*/) {
        long whiteQueens = white & queens;
        if (Long.highestOneBit(whiteQueens) == 0) {
            return;
        }
        long unoccupied = ~(white | black) & PERMITTED;
        long queen = Long.highestOneBit(whiteQueens);
        while (queen != 0) {
            whiteQueens ^= queen;
            int index = Long.numberOfLeadingZeros(queen);
            Fields from = fields[index];
            long rightDiagonal = RIGHT_DIAGONALS[index];
            long leftDiagonal = LEFT_DIAGONALS[index];
            long freeRightDiag = unoccupied & rightDiagonal;
            long freeLeftDiag = unoccupied & leftDiagonal;
            if (freeRightDiag == (rightDiagonal ^ queen)) {
                long targetField = Long.highestOneBit(freeRightDiag);
                while (targetField != 0) {
                    freeRightDiag ^= targetField;
                    addWhiteQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.highestOneBit(freeRightDiag);
                }
            } else {
                long targetField = Long.rotateRight(queen, 5);
                while ((targetField & freeRightDiag) != 0) {
                    addWhiteQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.rotateRight(targetField, 5);
                }
                targetField = Long.rotateRight(queen, -5);
                while ((targetField & freeRightDiag) != 0) {
                    addWhiteQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.rotateRight(targetField, -5);
                }
            }

            if (freeLeftDiag == (leftDiagonal ^ queen)) {
                long targetField = Long.highestOneBit(freeLeftDiag);
                while (targetField != 0) {
                    freeLeftDiag ^= targetField;
                    addWhiteQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.highestOneBit(freeLeftDiag);
                }
            } else {
                long targetField = Long.rotateRight(queen, 4);
                while ((targetField & freeLeftDiag) != 0) {
                    addWhiteQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.rotateRight(targetField, 4);
                }
                targetField = Long.rotateRight(queen, -4);
                while ((targetField & freeLeftDiag) != 0) {
                    addWhiteQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.rotateRight(targetField, -4);
                }
            }
            queen = Long.highestOneBit(whiteQueens);
        }
    }

    private void addWhiteQueenMove(long queen, long targetField, long white, long black, long queens,
            Fields from, int sourceIndex, List<Move> moves, int hashCode/*, int lockCode*/) {
        int targetIndex = Long.numberOfLeadingZeros(targetField);
        Fields to = fields[targetIndex];
        long newWhite = white ^ (queen | targetField);
        long newQueens = queens ^ (queen | targetField);
        BoardPosition position = new BoardPosition(newWhite, black, newQueens);
        hashCode ^= HASH_RANDOM_TABLE[sourceIndex][2] ^ HASH_RANDOM_TABLE[targetIndex][2];
        //lockCode ^= LOCK_RANDOM_TABLE[sourceIndex][2] ^ LOCK_RANDOM_TABLE[targetIndex][2];
        long lockCode = (newWhite+black+newQueens);
        position.setHashCode(hashCode);
        position.setLockCode(lockCode);
        //position.computeHashAndLockCode();
        String notation = from.toString() + "-" + to.toString();
        Move m = new Move(position, from, to, notation, notation, false);
        moves.add(m);
    }

    private void generateBlackQueenMoves(long white, long black, long queens, List<Move> moves,
            int hashCode/*, int lockCode*/) {
        long blackQueens = black & queens;
        if (Long.highestOneBit(blackQueens) == 0) {
            return;
        }
        long unoccupied = ~(white | black) & PERMITTED;
        long queen = Long.highestOneBit(blackQueens);
        while (queen != 0) {
            blackQueens ^= queen;
            int index = Long.numberOfLeadingZeros(queen);
            Fields from = fields[index];
            long rightDiagonal = RIGHT_DIAGONALS[index];
            long leftDiagonal = LEFT_DIAGONALS[index];
            long freeRightDiag = unoccupied & rightDiagonal;
            long freeLeftDiag = unoccupied & leftDiagonal;
            if (freeRightDiag == (rightDiagonal ^ queen)) {
                long targetField = Long.highestOneBit(freeRightDiag);
                while (targetField != 0) {
                    freeRightDiag ^= targetField;
                    addBlackQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.highestOneBit(freeRightDiag);
                }
            } else {
                long targetField = Long.rotateRight(queen, 5);
                while ((targetField & freeRightDiag) != 0) {
                    addBlackQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.rotateRight(targetField, 5);
                }
                targetField = Long.rotateRight(queen, -5);
                while ((targetField & freeRightDiag) != 0) {
                    addBlackQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.rotateRight(targetField, -5);
                }
            }

            if (freeLeftDiag == (leftDiagonal ^ queen)) {
                long targetField = Long.highestOneBit(freeLeftDiag);
                while (targetField != 0) {
                    freeLeftDiag ^= targetField;
                    addBlackQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.highestOneBit(freeLeftDiag);
                }
            } else {
                long targetField = Long.rotateRight(queen, 4);
                while ((targetField & freeLeftDiag) != 0) {
                    addBlackQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.rotateRight(targetField, 4);
                }
                targetField = Long.rotateRight(queen, -4);
                while ((targetField & freeLeftDiag) != 0) {
                    addBlackQueenMove(queen, targetField, white, black, queens, from, index, moves, hashCode/*, lockCode*/);
                    targetField = Long.rotateRight(targetField, -4);
                }
            }
            queen = Long.highestOneBit(blackQueens);
        }
    }

    private void addBlackQueenMove(long queen, long targetField, long white, long black, long queens,
            Fields from, int sourceIndex, List<Move> moves, int hashCode/*, int lockCode*/) {
        int targetIndex = Long.numberOfLeadingZeros(targetField);
        Fields to = fields[targetIndex];
        long newBlack = black ^ (queen | targetField);
        long newQueens = queens ^ (queen | targetField);
        BoardPosition position = new BoardPosition(white, newBlack, newQueens);
        hashCode ^= HASH_RANDOM_TABLE[sourceIndex][3] ^ HASH_RANDOM_TABLE[targetIndex][3];
        //lockCode ^= LOCK_RANDOM_TABLE[sourceIndex][3] ^ LOCK_RANDOM_TABLE[targetIndex][3];
        long lockCode = (white+newBlack+newQueens);
        position.setHashCode(hashCode);
        position.setLockCode(lockCode);
        //position.computeHashAndLockCode();
        String notation = from.toString() + "-" + to.toString();
        Move m = new Move(position, from, to, notation, notation, false);
        moves.add(m);
    }

    private void removeRedundantCaptures(List<Move> moves, int longestCapture) {
        for (int i = moves.size() - 1; i >= 0; i--) {
            if (moves.get(i).getNotation().length() < longestCapture) {
                moves.remove(i);
            }
        }
    }

    private void printSetFields(long state) {
        char[] c = Long.toBinaryString(state).toCharArray();
        int zeros = Long.numberOfLeadingZeros(state);
        int translation = (zeros > 0) ? zeros : 0;
        try {
            for (int i = c.length - 1; i >= 0; i--) {
                if (c[i] == '1') {
                    System.out.print(fields[i + translation] + ", ");
                }
            }
        } catch (IndexOutOfBoundsException e) {
            System.out.println("c size: " + c.length);
            System.out.println("zeros: " + zeros);
            System.out.println("translation: " + translation);
        }
        System.out.println();
    }
}
